package cn.pm.datasource.sharding;

import cn.pm.datasource.sharding.consts.RedisListenerTopicConst;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ToStringSerializer;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.MediaType;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * Create by spf on 2020/01/04
 * 自动建表扩展配置  增加独立配置类，调整配置方式
 */
@Configuration
@ConfigurationProperties("spring.shardingsphere.sharding.auto-create-table-config")
@ConditionalOnMissingBean(LoadAutoCreateTablesCacheRunner.class)
public class ShardingConfig {

    /**
     * 动态创建表时，需要通知的url
     */
    private List<String> autoCreateTablesUpdateCacheUrl = new ArrayList<>();

    /**
     * 按照半年分表时，初始化的年份
     */
    private int initMinYear;


    public List<String> getAutoCreateTablesUpdateCacheUrl() {
        return autoCreateTablesUpdateCacheUrl;
    }

    public void setAutoCreateTablesUpdateCacheUrl(List<String> autoCreateTablesUpdateCacheUrl) {
        this.autoCreateTablesUpdateCacheUrl = autoCreateTablesUpdateCacheUrl;
    }

    public int getInitMinYear() {
        return initMinYear;
    }

    public void setInitMinYear(int initMinYear) {
        this.initMinYear = initMinYear;
    }


    @Bean
    public LoadAutoCreateTablesCacheRunner LoadAutoCreateTablesCacheRunner(){
        return new LoadAutoCreateTablesCacheRunner();
    }

    @Bean
    public ShardingTableNameUtils ShardingTableNameUtils(){
        return new ShardingTableNameUtils();
    }

    /**
     * fastjson转换器
     */
    public static FastJsonHttpMessageConverter getFastJson() {
        //定义一个convert转换消息的对象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();

        //添加fastjson的配置信息
        FastJsonConfig fastJsonConfig = new FastJsonConfig();

        // JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        fastJsonConfig.setSerializerFeatures(
                // SerializerFeature.PrettyFormat,					//输出json格式化
                SerializerFeature.WriteDateUseDateFormat,        //日期类型默认使用默认格式化
                SerializerFeature.DisableCircularReferenceDetect//取消对象的循环引用
                // SerializerFeature.WriteMapNullValue				//值为null的字段，仍然输出
        );
        fastConverter.setFastJsonConfig(fastJsonConfig);

        //解决Long转json精度丢失的问题
        SerializeConfig serializeConfig = SerializeConfig.globalInstance;
        serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
        serializeConfig.put(Long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
        fastJsonConfig.setSerializeConfig(serializeConfig);

        //处理中文乱码问题
        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastConverter.setSupportedMediaTypes(fastMediaTypes);
        return fastConverter;
    }

    /**
     * 默认全局异常拦截Filter
     */
   /* @Bean
    public FilterRegistrationBean globalExceptionFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        GlobalExceptionFilter globalExceptionFilter = new GlobalExceptionFilter();
        registrationBean.setFilter(globalExceptionFilter);
        List<String> urlPatterns = new ArrayList<>();
        urlPatterns.add("/*");
        registrationBean.setUrlPatterns(urlPatterns);
        registrationBean.setOrder(-1);
        return registrationBean;
    }*/


    /**
     * 配置RedisTemplate  用于Redis连接
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setStringSerializer(new StringRedisSerializer());

        /**
         * 这里我们不使用FastJson的序列化器，因为在我们项目中很多地方都使用FastJson的注解标识非序列化
         * 这会跟Redis存储的期望值不一致
         */
        // redisTemplate.setValueSerializer(new FastJsonRedisSerializer(Object.class));
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        return redisTemplate;
    }

    /**
     * redis 发布订阅  注册监听者
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListener( RedisConnectionFactory connectionFactory){
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        // 定义监听类 与 监听的Topic 可以一对多
        container.addMessageListener(new ShardingRedisListener(),new ChannelTopic(RedisListenerTopicConst.AUTO_CREATE_TABLE_UPDATE_TOPIC));

        // 这里我们使用缓存线程池（60秒内线程可复用）
        // 因为Redis监听到的消息不频繁，如果长时间维护多个无用的线程，将会是资源浪费
        ThreadFactory threadFactory = new DefaultThreadFactory("RedisMessageListener", true);
        ExecutorService threadPoolExecutor = Executors.newCachedThreadPool(threadFactory);
        container.setTaskExecutor(threadPoolExecutor);
        return container;
    }

}
